package com.magenta.maxoptra.geocoding.decarta;

import com.magenta.maxoptra.geocoding.decarta.jaxb.*;
import com.magenta.maxoptra.gis.GeoException;
import com.magenta.maxoptra.gis.GeoAddress;
import org.apache.http.client.HttpClient;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by const Date: 25.03.2009 Time: 16:55:15
 * <p/>
 * Implementation of the deCarta geocode request.
 * <p/>
 *
 * @author Konstantin Pestrikov
 * @see DecartaWSRequest
 */
public class GeocodeRequest extends DecartaWSRequest<List<GeoAddress>> {
    private final String address;
    private final String language;
    private final String region;
    private final List<Float> accuracies;

    public GeocodeRequest(HttpClient client, String region, String address, String language, List<Float> accuracies) {
        super(client);
        this.region = region;
        this.address = address;
        this.accuracies = accuracies;
        this.language = language;
    }

    @Override
    protected List<GeoAddress> execute() throws GeoException {
        final List<GeoAddress> result = new ArrayList<GeoAddress>();
        GeocodeRequestType params = factory.createGeocodeRequestType();

//        params.setReturnFreeForm(true);
//        params.setReport(true);

        List<com.magenta.maxoptra.geocoding.decarta.jaxb.AddressType> addrs = params.getAddress();
        addrs.add(createFreeFormAddress(address, region, language));

        XLSMessage msg = newXLSMessage(factory.createGeocodeRequest(params), "GeocodeRequest");

        //post to http server
        XLSMessage resp = dataSource.executeRequest(msg);

        handleResponce(resp, new AddressHandler() {
            public void handle(GeocodedAddressType addr) {
                GeoAddress geoAddr = toGeoAddress(addr);
                if (accuracies != null) {
                    accuracies.add(addr.getGeocodeMatchCode().getAccuracy());
                }
                if (addr.getGeocodeMatchCode().getAccuracy() != 0.) {
                    result.add(geoAddr);
                }
            }
        });
        return result;
    }

    private GeoAddress toGeoAddress(GeocodedAddressType addr) {
        GeoAddress geoAddress = new GeoAddress();

        List<Double> posValue = addr.getPoint().getPos().getValue();
        geoAddress.setLatitude(posValue.get(0));
        geoAddress.setLongitude(posValue.get(1));

        AddressType address = addr.getAddress();

        StreetAddressType streetAddress = address.getStreetAddress();
        if (streetAddress != null) {
            String street = streetAddress.getStreet().getValue();
            geoAddress.setStreet(street);
        }

        String countryCode = address.getCountryCode();
        geoAddress.setCountry(countryCode);

        String postCode = address.getPostalCode();
        geoAddress.setZip(postCode);

        String freeFormAddress = address.getFreeFormAddress();
        geoAddress.setInfo(freeFormAddress);

        for (NamedPlaceType namedPlaceType : address.getPlace()) {
            switch (namedPlaceType.getType()) {
                case COUNTRY_SUBDIVISION:
                    geoAddress.setState(namedPlaceType.getValue());
                    break;
                case COUNTRY_SECONDARY_SUBDIVISION:
                    geoAddress.setCounty(namedPlaceType.getValue());
                    break;
                case MUNICIPALITY:
                    geoAddress.setCity(namedPlaceType.getValue());
                    break;
                case MUNICIPALITY_SUBDIVISION:
                    geoAddress.setDistrict(namedPlaceType.getValue());
                    break;
            }
        }

        //logger.info("accuracy: " + addr.getGeocodeMatchCode().getAccuracy());
        //logger.info("line string: " + addr.getLineString());

        return geoAddress;
    }

    private interface AddressHandler {
        void handle(GeocodedAddressType addr);
    }

    /*
        finds the addresses in the xml responce and passes them to handler 
     */
    private void handleResponce(XLSMessage resp, AddressHandler handler) throws GeoException {
        ResponseType response = (ResponseType) resp.getValue().getBody().get(0).getValue();
        if (response.getNumberOfResponses().intValue() > 0) {
            GeocodeResponseType parameters = (GeocodeResponseType) response.getResponseParameters().getValue();
            List responseList = parameters.getGeocodeResponseList();
            if (responseList.size() > 0) {
                GeocodeResponseListType geocodeResponseList = (GeocodeResponseListType) responseList.get(0);
                List<GeocodedAddressType> addresses = geocodeResponseList.getGeocodedAddress();
                for (GeocodedAddressType addr : addresses) {
                    handler.handle(addr);
                }
            }
        } else {
            handleErrors(response, getClass());
        }
    }
}
